

module dotmars.text.utf16;

import dotmars.text.encoding;
import dotmars.base.stdtypes;
import dotmars.base.stdexcept;
import dotmars.base.convert;
import dotmars.text.utf;

import dotmars.base.math;


//This module is still under developing

version(none) {


private final class UTF16Encoder : Encoder
{

	private CharBuffer!(char, 6) m_buf; 
	private UTF16Encoding m_encoding;

	public this(UTF16Encoding enc)
	in {
		assert(enc !is null);
	}
	body {
		reset();
	}

	public override void reset() {
		m_buf.reset();
	}

	public override uint encode(string str, Sink!(ubyte) sink, bool flush) 
	in {
		assert(sink !is null);
		assert(str !is null);
	}
	body {
		uint byteCount = 0;
		Sink!(wchar) wsink = (wchar c) {
			m_encoding.putWchar(c, sink);
			byteCount += 2;	
		};
		// Some chars left...
		foreach(char c; str)
			m_buf.putChar(c, (dchar d) { toUtf16(d, wsink); }) ;

		if(flush) reset();

		return byteCount;
	}
}

////////////////////////////////////////////////////////////////////////////////

private final class UTF16Decoder : Decoder 
{
	private struct State {
		wchar ch;
		int surrogate;
		ubyte left;
	}

	State m_state;

	private UTF16Encoding m_encoding;

	public this(UTF16Encoding enc) {
		m_encoding = enc;
	}

	public override void reset() {
		m_state.surrogate = -1;
	}

	public override uint decode(ubyte[] bytes, Sink!(char) sink, bool flush) {
		assert(false);
		return 0;
	}
}

////////////////////////////////////////////////////////////////////////////////

class UTF16Encoding : Encoding
{
	const ubyte[2] UTF16BigEndian = [0xFE, 0xFF];
	const ubyte[2] UTF16LittleEndian = [0xFF, 0xFE];


	private bool m_bigEndian;
	private bool m_byteOrderMark;

	public alias Encoding.decode decode;
	public alias Encoding.encode encode;

	public this(bool bigEndian = false, bool byteOrderMark = true)
	{
		m_bigEndian = bigEndian;
		m_byteOrderMark = byteOrderMark;
	}

	public override Encoding clone(){
		auto e = new UTF16Encoding();
		e = this;
		return e;
	}

	public override string encodingName() {
		return "UTF-16";
	}

	public override bool isSingleByte() {
		return false;
	}

	public override ubyte[] preamble() {
		return m_bigEndian ? UTF16BigEndian : UTF16LittleEndian;
	}

	public override Encoder getEncoder() {
		return new UTF16Encoder(this);
	}

	public override Decoder getDecoder() {
		return new UTF16Decoder(this);
	}

	public override uint getByteCount(string chars)
	in{
		assert(chars !is null);
	}
	body {
		uint count = 0;
		each( chars, (dchar d) {
			count += cast(uint)(toUtf16(d, null) * 2);
		});
		return count;
	}

	public override uint getCharCount(ubyte[] bytes) 
	in{
		assert(bytes !is null);
	}
	body{
		uint count = 0;
		assert(false);
		
		return count;
	}

	public override uint getMaxByteCount(uint charCount){
		return charCount * 2;
	}

	public override uint getMaxCharCount(uint byteCount) {
		return byteCount / 2;
	}

	public override uint decode(ubyte[] bytes, Sink!(char) sink) 
	in{
		assert(bytes !is null);
	}
	body {
		assert(false);
		return 0;
	}

	public override uint encode(string chars, Sink!(ubyte) sink)
	in{
		assert(chars !is null);
		assert(sink !is null);
	}
	body {
		size_t pos = 0;
		size_t result = 0;

		Sink!(wchar) wsink = 
			(wchar c) { putWchar(c, sink); result += 2; };

		each(chars, (dchar d) {
			toUtf16(d, wsink);
		});
		return cast(uint)result;
	} 

	private void putWchar(wchar c, Sink!(ubyte) sink)
	{
		version(LittleEndian) {
			if(m_bigEndian)
				c = BitConverter.toBigEndian(c);
		}

		version(BigEndian) {
			if(!m_bigEndian) 
				c = BitConverter.toLittleEndian(c);
		}
		ubyte[] bytes = BitConverter.encode(c);	
		sink(bytes[0]);
		sink(bytes[1]);
	}
}

}//version(none)
